import * as THREE from 'three'
import { gsap } from 'gsap'

export class LineBuffer {
  constructor (options) {
    this.options = options
    this.geometry = this.getGeometry(options)
    this.mesh = null
    this.animation = null
    this.currentProgress = 0
    this.callback = options.callback
  }

  // ===== 初始化几何：position + uv 一次性齐全 =====
  getGeometry (options) {
    const geometry = new THREE.BufferGeometry()
    const { positions, uvs } = this.buildStrip(options, 1) // progress=1：完整几何
    geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
    geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2))
    geometry.setDrawRange(0, positions.length / 3)
    geometry.computeBoundingSphere()
    return geometry
  }

  // ===== 创建可动画的网格 =====
  createAnimatedMesh (material) {
    this.mesh = new THREE.Mesh(this.geometry, material)

    // 透明渐显需要这些设置以避免“看不到/被自身遮挡”的错觉
    material.transparent = true
    material.opacity = 0
    material.depthWrite = false
    material.depthTest = true
    material.side = THREE.DoubleSide
    // 如若紧贴地面想避免闪烁，可打开以下偏移：
    // material.polygonOffset = true
    // material.polygonOffsetFactor = -1
    // material.polygonOffsetUnits  = -1

    // 需要的话，动画期可禁用视锥裁剪以排除包围体误差：
    // this.mesh.frustumCulled = false

    this.mesh.userData = { progress: 0 }
    return this.mesh
  }

  // ===== 生长动画 =====
  animateGrowth (duration = 1, delay = 0, ease = 'power2.out') {
    if (!this.mesh) {
      console.warn('Mesh not created. Call createAnimatedMesh() first.')
      return
    }
    if (this.animation) this.animation.kill()

    this.mesh.userData.progress = 0
    this.mesh.material.opacity = 0

    this.animation = gsap.to(this.mesh.userData, {
      progress: 1,
      duration,
      delay,
      ease,
      onUpdate: () => {
        this.updateGeometry(this.mesh.userData.progress)
        this.mesh.material.opacity = this.mesh.userData.progress
      },
      onComplete: () => {
        this.animation = null
        this.callback?.()
      }
    })
    return this.animation
  }

  // ===== 进度更新几何（同步 position + uv + drawRange）=====
  updateGeometry (progress) {
    const { positions, uvs, count } = this.buildStrip(this.options, progress)

    // 同步属性，确保 attribute 数量一致
    this.geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
    this.geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2))
    this.geometry.setDrawRange(0, count)
    this.geometry.computeBoundingSphere()
    // 带状条通常不依赖法线光照，可省略 computeVertexNormals()
    this.geometry.attributes.position.needsUpdate = true
    this.geometry.attributes.uv.needsUpdate = true
  }

  // ===== 内部：按 progress 构建带状三角形条 =====
  buildStrip ({ path, point = 50, pointsScale = 1 }, progress = 1) {
    // 曲线与采样
    const curve = new THREE.CatmullRomCurve3(path)
    curve.curveType = 'catmullrom'
    curve.tension = 0.2
    const pts = curve.getPoints(point)

    // 至少两个点才能形成一段
    const visiblePoints = Math.max(2, Math.ceil(pts.length * THREE.MathUtils.clamp(progress, 0, 1)))
    const segs = visiblePoints - 1
    if (segs <= 0) {
      return {
        positions: new Float32Array(0),
        uvs: new Float32Array(0),
        count: 0
      }
    }

    // 计算切线与带宽方向法线（避免与 up 平行退化）
    const normals = new Array(visiblePoints)
    for (let i = 0; i < visiblePoints; i++) {
      const prev = pts[Math.max(0, i - 1)]
      const next = pts[Math.min(visiblePoints - 1, i + 1)]
      const tangent = new THREE.Vector3().subVectors(next, prev).normalize()

      const up = new THREE.Vector3(0, 1, 0)
      let n = new THREE.Vector3().crossVectors(up, tangent)
      if (n.lengthSq() < 1e-6) n = new THREE.Vector3().crossVectors(new THREE.Vector3(0, 0, 1), tangent)
      normals[i] = n.normalize()
    }

    // 统计长度用于 U（0~1）
    let totalLen = 0
    const segLen = new Array(segs)
    for (let i = 0; i < segs; i++) {
      segLen[i] = pts[i + 1].distanceTo(pts[i])
      totalLen += segLen[i]
    }

    // 分配缓冲（每段 2 三角形 = 6 顶点）
    const positions = new Float32Array(segs * 6 * 3)
    const uvs = new Float32Array(segs * 6 * 2)

    let pOfs = 0
    let uvOfs = 0
    let acc = 0

    // 逐段填充
    for (let i = 0; i < segs; i++) {
      const a = pts[i]
      const b = pts[i + 1]
      const n0 = normals[i]
      const n1 = normals[i + 1]

      const aL = new THREE.Vector3().copy(a).addScaledVector(n0, +pointsScale)
      const aR = new THREE.Vector3().copy(a).addScaledVector(n0, -pointsScale)
      const bL = new THREE.Vector3().copy(b).addScaledVector(n1, +pointsScale)
      const bR = new THREE.Vector3().copy(b).addScaledVector(n1, -pointsScale)

      const u0 = totalLen > 0 ? acc / totalLen : 0
      acc += segLen[i]
      const u1 = totalLen > 0 ? acc / totalLen : 1

      // tri1: aL, aR, bL
      positions[pOfs++] = aL.x; positions[pOfs++] = aL.y; positions[pOfs++] = aL.z
      positions[pOfs++] = aR.x; positions[pOfs++] = aR.y; positions[pOfs++] = aR.z
      positions[pOfs++] = bL.x; positions[pOfs++] = bL.y; positions[pOfs++] = bL.z

      uvs[uvOfs++] = u0; uvs[uvOfs++] = 0
      uvs[uvOfs++] = u0; uvs[uvOfs++] = 1
      uvs[uvOfs++] = u1; uvs[uvOfs++] = 0

      // tri2: aR, bR, bL
      positions[pOfs++] = aR.x; positions[pOfs++] = aR.y; positions[pOfs++] = aR.z
      positions[pOfs++] = bR.x; positions[pOfs++] = bR.y; positions[pOfs++] = bR.z
      positions[pOfs++] = bL.x; positions[pOfs++] = bL.y; positions[pOfs++] = bL.z

      uvs[uvOfs++] = u0; uvs[uvOfs++] = 1
      uvs[uvOfs++] = u1; uvs[uvOfs++] = 1
      uvs[uvOfs++] = u1; uvs[uvOfs++] = 0
    }

    return {
      positions,
      uvs,
      count: segs * 6
    }
  }

  // ===== 资源清理 =====
  dispose () {
    if (this.animation) {
      this.animation.kill()
      this.animation = null
    }
    if (this.mesh) {
      if (this.mesh.parent) this.mesh.parent.remove(this.mesh)
      if (this.mesh.material) this.mesh.material.dispose()
    }
    if (this.geometry) this.geometry.dispose()

    this.mesh = null
    this.geometry = null
    this.callback = null
  }
}
